package com.daoliuhe.scaffold.config;

import com.daoliuhe.scaffold.core.RedisCacheManager;
import com.daoliuhe.scaffold.core.RedisSessionDAO;
import com.daoliuhe.scaffold.core.RetryLimitHashedCredentialsMatcher;
import com.daoliuhe.scaffold.core.SimpleLDAPPasswordMatcher;
import com.daoliuhe.scaffold.filter.ForceLogoutFilter;
import com.daoliuhe.scaffold.filter.RememberMeFilter;
import com.daoliuhe.scaffold.realms.ShiroDbRealm;
import com.daoliuhe.scaffold.realms.SimpleLDAPRealm;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 * ${DESCRIPTION}
 *
 * @author 21829
 * @create 2018-02-02 15:19
 **/
@Configuration
public class ShiroConfig {

    @Bean
    @DependsOn({"securityManager","formAuthenticationFilter","rememberMeFilter", "forceLogoutFilter"})
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //Shiro的核心安全接口，这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        //filters.put("authc", formAuthenticationFilter());
        filters.put("user", rememberMeFilter());
        filters.put("forceLogout", forceLogoutFilter());
        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //注意过滤器配置顺序 不能颠倒
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了，登出后跳转配置的loginUrl
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/login/**", "anon");
        //自定义加载权限资源关系
        /*
        List<Resources> resourcesList = resourcesService.queryAll();
        for(Resources resources:resourcesList){

            if (StringUtil.isNotEmpty(resources.getResurl())) {
                String permission = "perms[" + resources.getResurl()+ "]";
                filterChainDefinitionMap.put(resources.getResurl(),permission);
            }
        }
        */
        filterChainDefinitionMap.put("/**", "authc");
        //配置shiro默认登录界面地址，前后端分离中登录界面跳转应由前端路由控制，后台仅返回json数据
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/login/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean(name = "forceLogoutFilter")
    public ForceLogoutFilter forceLogoutFilter() {
        return new ForceLogoutFilter();
    }

    @Bean(name = "rememberMeFilter")
    public RememberMeFilter rememberMeFilter() {
        return new RememberMeFilter();
    }

    @Bean(name = "formAuthenticationFilter")
    public FormAuthenticationFilter formAuthenticationFilter() {
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        formAuthenticationFilter.setUsernameParam("username");
        formAuthenticationFilter.setPasswordParam("password");
        formAuthenticationFilter.setLoginUrl("/login");
        formAuthenticationFilter.setRememberMeParam("scaffoldRememberMe");
        return formAuthenticationFilter;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }

    @Bean(name = "securityManager")
    @DependsOn({"memberRealm", "redisCacheManager", "sessionManager", "rememberManager"})
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealms(memberRealm());
        securityManager.setCacheManager(redisCacheManager());
        securityManager.setSessionManager(sessionManager());
        securityManager.setRememberMeManager(rememberManager());
        return securityManager;
    }

    @Bean
    public RetryLimitHashedCredentialsMatcher credentialsMatcher() {
        RetryLimitHashedCredentialsMatcher matcher = new RetryLimitHashedCredentialsMatcher(redisCacheManager());
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(2);
        matcher.setStoredCredentialsHexEncoded(true);
        return matcher;
    }

    @Bean
    public SimpleLDAPPasswordMatcher simpleLDAPPasswordMatcher() {
        return new SimpleLDAPPasswordMatcher();
    }

    @Bean
    public Collection<Realm> memberRealm() {
        List<Realm> realms = new ArrayList<Realm>();
        //数据库账号密码校验
        ShiroDbRealm dbRealm = shiroDbRealm();
        dbRealm.setCredentialsMatcher(credentialsMatcher());
        dbRealm.setCachingEnabled(true);
        realms.add(dbRealm);

        //域控校验
        SimpleLDAPRealm simpleLDAPRealm = simpleLDAPRealm();
        simpleLDAPRealm.setCredentialsMatcher(simpleLDAPPasswordMatcher());
        simpleLDAPRealm.setCachingEnabled(true);
        realms.add(simpleLDAPRealm);

        return realms;
    }

    @Bean
    public ShiroDbRealm shiroDbRealm(){
        return new ShiroDbRealm();
    }

    @Bean
    public SimpleLDAPRealm simpleLDAPRealm(){
        return new SimpleLDAPRealm();
    }

    @Bean
    public RedisCacheManager redisCacheManager() {
        return new RedisCacheManager();
    }

    @Bean
    public RememberMeManager rememberManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("scaffold-RememberMe");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(3600);
        rememberMeManager.setCookie(cookie);
        byte[] cipherKey = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");
        rememberMeManager.setCipherKey(cipherKey);
        return rememberMeManager;
    }

    /**
     * 会话ID生成器
     */
    @Bean
    public SessionIdGenerator getSessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean(name = "sessionDAO")
    public SessionDAO sessionDAO() {
        // 使用Redis进行Session管理
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        return sessionDAO;
    }

    //自定义sessionManager
    @Bean
    @DependsOn({"sessionIdCookie", "sessionDAO"})
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(7200000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionDAO(sessionDAO());
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        return sessionManager;
    }

    @Bean(name = "sessionIdCookie")
    public SimpleCookie sessionIdCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("scaffold_id");
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }
}
